<?php
/**
 * $Id: states_machine.php 11 2010-11-24 22:10:53Z LEON $
 * $Author: LEON $
 * $LastChangedDate: 2010-11-24 22:10:53 +0000 (Wed, 24 Nov 2010) $
 * $Revision: 11 $
 */
namespace cmFly\utils;
abstract class states_machine {
    protected $states_history;
    protected $current_state;
    protected $states=Array();
    protected $history=Array();
    protected $current_pos=0;
    protected $buf;
    const STATE_GET_WORD=1;
    const STATE_GET_BYTE=2;
    const STATE_GET_NUM=3;
    protected $white_spaces=Array(' ',"\t","\r","\n");
    final public function __construct($string) {
	$this->buf=$string;
	$this->init();
    }
    protected function add_state($state_type,$state_name,$parameters=NULL) {
	if(!$parameters) {
	    $parameters=Array();
	}
	$this->states[$state_name]=Array($state_type,$parameters);
    }
    public function set_current_state($state) {
	if(!isset($this->states[$state])) {
	    throw new cmFly_exception('Can\'t find state '.$state.'!');
	}
	$this->history[]=Array($this->current_pos,$this->current_state);
	$this->current_state=$state;
    }
    public function get_current_state() {
	return $this->current_state;
    }
    public function get_current_vars() {
	return $this->states[$this->get_current_state()][1];
    }
    public function process() {
	if($this->current_pos>=strlen($this->buf)) {
	    return true;
	}
	list($state,$parameters)=$this->states[$this->get_current_state()];
	$stop_chars=Array();
	if(isset($parameters['stop_chars'])) {
	    $stop_chars=$parameters['stop_chars'];
	}
	$result="";
	$pos=&$this->current_pos;
	$len=strlen($this->buf);
	while($pos<$len) {
	    $char=$this->buf[$pos];
	    $is_white_space=in_array($char,$this->white_spaces);
	    if($is_white_space && empty($result)) {
		$pos++;
		continue;
	    }
	    switch($state) {
		case self::STATE_GET_WORD:
		    if($is_white_space) {
			$pos++;
			return $result;
		    }
		    if(!empty($result) && in_array($char,$stop_chars)) {
			return $result;
		    }
		    $result.=$char;
		    break;
		case self::STATE_GET_BYTE:
		    $pos++;
		    return $char;
		    break;
		case self::STATE_GET_NUM:
		    if(!is_numeric($char)) {
			if(empty($result)) {
			    throw new cmFly_exception('Numeric not found!');
			}
			return $result;
		    }
		    $result.=$char;
		    break;
	    }
	    $pos++;
	}
	return $result;
    }
    protected function set_pos($pos) {
	return $this->current_pos=$pos;
    }
    public function back() {
	if(!count($this->history)) {
	    throw new cmFly\system\exception('History list is empty!');
	}
	list($pos,$state)=array_pop($this->history);
	$this->set_pos($pos);
	$this->set_current_state($state);
    }
    abstract protected function init();
}
?>